package com.autumn.platform.web.resulthandler.wrapper.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.MethodParameter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import com.autumn.platform.core.Utils;
import com.autumn.platform.core.context.response.IResponse;

public class ExceptionResultWrapper extends MapResultWrapper {

    public ExceptionResultWrapper() {
        this.setOrder(HIGHEST_PRECEDENCE);
    }

    @Override
    public boolean supports(Object result, MethodParameter resultType) {
        return result instanceof Throwable;
    }

    @Override
    protected void doWrap(Map<String, Object> wrapper, Object value) {
        String valueProperty = getValuePropertyName();
        IResponse iStatus = super.getStatus();
        if (value instanceof Throwable && !Utils.isBlank(valueProperty) && null != iStatus) {
            if (value instanceof HttpRequestMethodNotSupportedException) {
                this.doWrapHttpRequestMethodNotSupportedException(wrapper, valueProperty, iStatus, (HttpRequestMethodNotSupportedException) value);
            } else if (value instanceof HttpMediaTypeNotSupportedException) {
                this.doWrapHttpMediaTypeNotSupportedException(wrapper, valueProperty, iStatus, (HttpMediaTypeNotSupportedException) value);
            } else if (value instanceof HttpMediaTypeNotAcceptableException) {
                this.doWrapHttpMediaTypeNotAcceptableException(wrapper, valueProperty, iStatus, (HttpMediaTypeNotAcceptableException) value);
            } else if (value instanceof MissingPathVariableException) {
                this.doWrapMissingPathVariableException(wrapper, valueProperty, iStatus, (MissingPathVariableException) value);
            } else if (value instanceof MissingServletRequestParameterException) {
                this.doWrapMissingServletRequestParameterException(wrapper, valueProperty, iStatus, (MissingServletRequestParameterException) value);
            } else if (value instanceof ServletRequestBindingException) {
                this.doWrapServletRequestBindingException(wrapper, valueProperty, iStatus, (ServletRequestBindingException) value);
            } else if (value instanceof ConversionNotSupportedException) {
                this.doWrapConversionNotSupportedException(wrapper, valueProperty, iStatus, (ConversionNotSupportedException) value);
            } else if (value instanceof TypeMismatchException) {
                this.doWrapTypeMismatchException(wrapper, valueProperty, iStatus, (TypeMismatchException) value);
            } else if (value instanceof HttpMessageNotReadableException) {
                this.doWrapHttpMessageNotReadableException(wrapper, valueProperty, iStatus, (HttpMessageNotReadableException) value);
            } else if (value instanceof HttpMessageNotWritableException) {
                this.doWrapHttpMessageNotWritableException(wrapper, valueProperty, iStatus, (HttpMessageNotWritableException) value);
            } else if (value instanceof MethodArgumentNotValidException) {
                this.doWrapMethodArgumentNotValidException(wrapper, valueProperty, iStatus, (MethodArgumentNotValidException) value);
            } else if (value instanceof MissingServletRequestPartException) {
                this.doWrapMissingServletRequestPartException(wrapper, valueProperty, iStatus, (MissingServletRequestPartException) value);
            } else if (value instanceof BindException) {
                this.doWrapBindException(wrapper, valueProperty, iStatus, (BindException) value);
            } else if (value instanceof NoHandlerFoundException) {
                this.doWrapNoHandlerFoundException(wrapper, valueProperty, iStatus, (NoHandlerFoundException) value);
            } else if (value instanceof AsyncRequestTimeoutException) {
                this.doWrapAsyncRequestTimeoutException(wrapper, valueProperty, iStatus, (AsyncRequestTimeoutException) value);
            } else {
                this.doWrapThrowable(wrapper, valueProperty, iStatus, (Throwable) value);
            }
        }
    }

    protected void doWrapHttpRequestMethodNotSupportedException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, HttpRequestMethodNotSupportedException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapHttpMediaTypeNotSupportedException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, HttpMediaTypeNotSupportedException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapHttpMediaTypeNotAcceptableException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, HttpMediaTypeNotAcceptableException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapMissingPathVariableException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, MissingPathVariableException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapMissingServletRequestParameterException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, MissingServletRequestParameterException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapServletRequestBindingException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, ServletRequestBindingException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapConversionNotSupportedException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, ConversionNotSupportedException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapTypeMismatchException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, TypeMismatchException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapHttpMessageNotReadableException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, HttpMessageNotReadableException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapHttpMessageNotWritableException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, HttpMessageNotWritableException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapMethodArgumentNotValidException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, MethodArgumentNotValidException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapMissingServletRequestPartException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, MissingServletRequestPartException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapNoHandlerFoundException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, NoHandlerFoundException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapAsyncRequestTimeoutException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, AsyncRequestTimeoutException exception) {
        this.doWrapThrowable(wrapper, valueProperty, iStatus, exception);
    }

    protected void doWrapBindException(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, BindException exception) {
        String validateErrorsPropertyName = super.getValidateErrorsPropertyName();
        if (!Utils.isBlank(validateErrorsPropertyName)) {
            BindingResult result = exception.getBindingResult();
            StringBuilder msg = new StringBuilder();
            List<ValidateError> msgs = new ArrayList<ValidateError>();
            if (result.hasFieldErrors()) {
                for (FieldError error : result.getFieldErrors()) {
                    msgs.add(new ValidateError(error.getField(), false, error.getDefaultMessage()));
                    msg.append(",").append(error.getDefaultMessage());
                }
            }
            if (result.hasGlobalErrors()) {
                for (ObjectError error : result.getGlobalErrors()) {
                    msgs.add(new ValidateError(error.getObjectName(), true, error.getDefaultMessage()));
                    msg.append(",").append(error.getDefaultMessage());
                }
            }
            if (!Utils.isBlank(msg.toString())) {
                wrapper.put(super.getMessagePropertyName(), msg.substring(1));
            }
            wrapper.put(validateErrorsPropertyName, msgs);
        }
    }

    protected void doWrapThrowable(Map<String, Object> wrapper, String valueProperty, IResponse iStatus, Throwable throwable) {
        String property = super.getMessagePropertyName();
        String message = throwable.getLocalizedMessage();
        if (null != message && null != property /* !message.equals(status.getMessage()) */) {
            wrapper.put(property, message);
        }
    }

    public class ValidateError {
        private String name;
        private boolean global;
        private String message;

        public ValidateError(String name, boolean global, String message) {
            super();
            this.name = name;
            this.global = global;
            this.message = message;
        }

        public String getName() {
            return name;
        }

        public boolean isGlobal() {
            return global;
        }

        public String getMessage() {
            return message;
        }
    }
}
